import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class TxDialog extends JFrame {

    private String myPubKey;
    private String mySigKey;
    private List<Map.Entry<String, String>> shareDataList;
    private Chain myBlockChain;
    private List<Transaction> myTxIn;
    private List<Transaction> myTxOut;
    private List<Transaction> myUTXO;
    private List<Boolean> isSpent;

    public TxDialog(List<Map.Entry<String, String>> shareDataList, Chain myBlockChain, List<Boolean> isSpent){
        this.shareDataList = shareDataList;
        this.myBlockChain = myBlockChain;
        this.myPubKey = Utils.getValueByKey(shareDataList, "publicKey");
        this.myPubKey = Utils.getValueByKey(shareDataList, "privateKey");
        this.isSpent = isSpent;
        this.myTxIn = Utils.findMyTxIn(myBlockChain,myPubKey);
        this.myTxOut = Utils.findMyTxOut(myBlockChain,myPubKey);
        this.myUTXO = new ArrayList<>(); // 不需要父组件传入，可以通过myTxIn和myTxOut+isSpent计算出。故直接初始化

        // 创建 JFrame
        setTitle("交易");
        setSize(600, 400);

        // 创建面板
        JPanel mainPanel = new JPanel();
        mainPanel.setLayout(new BorderLayout());

        // 第一部分：发起交易按钮和两个输入框
        JPanel initiateTransactionPanel = new JPanel();
        initiateTransactionPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 10));  // 左对齐，10为水平间隙，10为垂直间隙

        // 创建标签和输入框
        JLabel recipientAddressLabel = new JLabel("收款方地址: ");
        JTextField recipientAddressField = new JTextField(20);  // 设置宽度

        JLabel amountLabel = new JLabel("交易金额   : ");
        JTextField amountField = new JTextField(20);  // 设置宽度

        JButton initiateButton = new JButton("发起交易");

        // 调整按钮宽度使其符合父容器宽度
        initiateButton.setPreferredSize(new Dimension(550, 60));  // 设置宽度为父容器宽度（400）
        // 或者使用按钮的 layout 来占满父容器宽度，可以通过设置布局来实现

        // 将标签和输入框添加到面板
        initiateTransactionPanel.add(recipientAddressLabel);
        initiateTransactionPanel.add(recipientAddressField);
        initiateTransactionPanel.add(amountLabel);
        initiateTransactionPanel.add(amountField);
        initiateTransactionPanel.add(initiateButton);


        // 第二部分：验收交易按钮和交易列表
        JPanel acceptTransactionPanel = new JPanel();
        acceptTransactionPanel.setLayout(new BorderLayout());

        JButton acceptButton = new JButton("验收交易");

        // 创建交易列表
        DefaultListModel<String> transactionListModel = new DefaultListModel<>();

        // 添加一些模拟交易项到列表
//        transactionListModel.addElement("交易 1: 发款方地址 1, 金额 100 BTC");
//        transactionListModel.addElement("交易 2: 发款方地址 2, 金额 200 BTC");
        myTxIn = Utils.findMyTxIn(myBlockChain,myPubKey);
        int txCount = 1;

        for(Transaction tx : myTxIn){
            String txStr = String.format("交易 %d：发款方地址 %s，金额 %s BTC", txCount++, tx.from, tx.amount);
            transactionListModel.addElement(txStr);
        }
        JList<String> transactionList = new JList<>(transactionListModel);
        transactionList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);  // 允许单选

        // 添加组件到面板
        acceptTransactionPanel.add(new JScrollPane(transactionList), BorderLayout.CENTER);
        acceptTransactionPanel.add(acceptButton, BorderLayout.SOUTH);

        // 添加两部分面板到主面板
        mainPanel.add(initiateTransactionPanel, BorderLayout.NORTH);
        mainPanel.add(acceptTransactionPanel, BorderLayout.CENTER);

        // 设置 JFrame 中的内容
        add(mainPanel);
        JFrame frame = this;
        setLocationRelativeTo(null);  // 设置窗体居中显示

        // 发起交易按钮事件
        initiateButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String recipientAddress = recipientAddressField.getText();
                String amount = amountField.getText();
                if (!recipientAddress.isEmpty() && !amount.isEmpty()) {
                    /**
                     * 发起交易需要判断：
                     * 1我的UTXO中是否有足够余额？？
                     * 2如何筹集这笔支出的钱？？
                     * 3发起这笔交易是否有零钱剩余？？
                     * */
                    // 判断是否够钱
                    int totalMoney = Integer.parseInt(amount);
                    int sum = 0;
                    for(Transaction utxo : myUTXO){
                        sum += utxo.amount;
                    }
                    if(sum < totalMoney){
                        JOptionPane.showMessageDialog(frame, "钱包余额不足！");
                        return;
                    }else{
                        /**  筹齐这笔支出的钱
                         * 可选策略：
                         * 1从最小面额开始凑集
                         * 2直接使用最大能覆盖的面额，若无，则从最大面额开始凑集
                         * */
                        while(totalMoney>0){
                            int index = getMinTxIndex(); // 策略1
                            isSpent.set(index, true); // isSpent 即被用来筹集的交易序号
                            totalMoney -= myUTXO.get(index).amount;
                            if(totalMoney == 0){
                                JOptionPane.showMessageDialog(frame, "钱包中存在一些恰好能组成符合支出总金额的交易");
                            }else if(totalMoney < 0){
                                JOptionPane.showMessageDialog(frame, "本次交易有零钱返还！");
                                int change = Math.abs(totalMoney);
                                // 创建一个新的交易，自己支付给自己剩余的零钱
                                sendMyTxToChainPool(myPubKey, String.valueOf(change));
                                isSpent.add(false);
                            }
                        }
                    }
                    sendMyTxToChainPool(recipientAddress,amount);
                    // 表单置空
                    recipientAddressField.setText("");
                    amountField.setText("");
                } else {
                    JOptionPane.showMessageDialog(frame, "请填写完整的收款方地址和交易金额");
                }
            }
        });

        // 验收交易按钮事件
        /**
         * 验收交易：
         * 此时等待验收的交易已经成功推到链的交易池，但是没有被区块收录
         * （你作为一个正在验收交易的用户，在将交易金额收入钱包前需要做的就是验证这个交易的签名）
         * */
        acceptButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String selectedTxStr = transactionList.getSelectedValue();
                int selectedTxIndex = transactionList.getSelectedIndex(); // 序号从零开始
                System.out.println(selectedTxIndex);
                Transaction txToVerify = myTxIn.get(selectedTxIndex); // myTxIn已经是从在链上所有区块收录的交易中识别出来的了，不应该再把它推到链上的交易池
                try {
                    if (selectedTxStr==null){
                        JOptionPane.showMessageDialog(frame, "请先选中一个交易进行验收");
                    }
                    if (txToVerify.validate()){ // 对于一个选中的交易记录，通过验证其签名实现验证
                        // myBlockChain.addTxToPool(txToVerify); // 错误的处理。应该是把被验证的交易放入自己的钱包中而不是链的交易池中
                        // 收到钱包
                        myUTXO.add(txToVerify);
                        isSpent.add(false);
                        JOptionPane.showMessageDialog(frame, "交易验收成功： " + selectedTxStr + "交易序号为：" + selectedTxIndex);
                        // 从列表中去掉被验收成功的交易
                        transactionListModel.remove(selectedTxIndex);
                    }else {
                        JOptionPane.showMessageDialog(frame, "交易验收失败： " + selectedTxStr + "交易序号为：" + selectedTxIndex);
                    }
                    if (selectedTxStr != null) {
                    } else {
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        });
    }

    // 即myTxIn中的第几个交易被用来筹钱
    private int getMinTxIndex() {
        int minMoney = 9999999;
        int index = 0;
        for(int i = 0; i<myUTXO.size() ;i++){
            Transaction tx = myUTXO.get(i);
            if(minMoney > tx.amount){
                minMoney = tx.amount;
                index = i;
            }
        }
        return index;
    }

    // 从一个字符串形式的交易记录中，分离出交易from方公钥
    private String parsePubKeyStr(String selectedTxStr) {
        String[] parts = selectedTxStr.split(": ");
        if(parts.length>1){
            String secondPart = parts[1];
            String[] split = secondPart.split(",");
            if(split.length>1){
                String fromStr = split[0].replace("发款方地址 ","");
                System.out.println(fromStr);
                return fromStr;
            }
        }
        return "error to parse string";
    }

    // 将当前表单中数据打包为一个交易请求，添加到链的交易池
    private void sendMyTxToChainPool(String recipientAddress, String amount) {
        try {
            Transaction txForSending = new Transaction(this.myPubKey, recipientAddress, Integer.parseInt(amount));
            txForSending.sign(this.mySigKey);
            // 添加到链的交易池，等待出块收录
            if(txForSending != null){
                myBlockChain.addTxToPool(txForSending);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                List<Map.Entry<String, String>> testList = new ArrayList<>(); // 空
                Map.Entry<String,String> test1 = Map.entry("publicKey", "MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCCNIQ6cHSS/6VlOZbciZx0n+7GHGdAEitRcJmZ38OJj+Q==");
                Map.Entry<String,String> test2 = Map.entry("privateKey", "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUErt/vpIm0xbzEPuQvC5ZCx5PwOepVGzBZK5fRVQ3r4ROKZjFZbwOoLiW1wzhcbxBv7kNge3HWqY6VKlMLqGQw==");
                testList.add(test1);
                testList.add(test2);
                new TxDialog(testList,new Chain(), new ArrayList<>()).setVisible(true);
            }
        });
    }
}
